/*
 ============================================================================
 Name        : SimpleIO.c
 Version     : 4.1
 Description : Coding sample for performing simple write/read operations to HCP.

               This sample uses the libcurl open-source library to perform the
               HTTP communication to HCP.

 License     :
               Copyright (c) 2011 Hitachi Data Systems, Inc.

               Permission is hereby granted to  this software and associated
               documentation files (the "Software"), subject to the terms and
               conditions of the Sample Source Code License (SSCL) delivered
               with this Software. If you do not agree to the terms and
               conditions of the SSCL,

                 (i)  you must close this file and delete all copies of the
                      Software, and
                 (ii) any permission to use the Software is expressly denied.

 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#include <curl/curl.h>

#include <openssl/evp.h>

#include "hcpUtils.h"

#define INPUT_FILE_NAME "simpleio.txt.toHCP"
#define OUTPUT_FILE_NAME "simpleio.txt.fromHCP"

#define HCP_PROTOCOL "https"
#define HCP_FILE_PATH "rest/examples/simpleio.txt"

/**
 * This routine performs and HTTP PUT of an object.
 */
int do_write(CURL *curl, const char *srcFilePath, const char *dstFilePath, const char *authHeader) {
	int retval = EXIT_SUCCESS;
	FILE *srcFile = NULL;
	struct curl_slist *httpHeaderList=NULL;

	/***
	 *** Setup CURL reference for performing the HTTP PUT.
	 ***/
	curl_easy_reset(curl);

	// Bypass any SSL Key checks because we are not using site specific keys.
    //  Not needed if only doing HTTP
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);

	// Provide the response header call back function.  This is just for
	//  debugging/development purposes. It will cause the HTTP headers for
	//  the response to be output to stdout.
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &headerProcessor);

	// Indicate this CURL operation is an HTTP PUT.
	curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);  // Note: CURLOPT_PUT is depreciated in 7.12.1

	// Set the URL to the destination file on HCP
	curl_easy_setopt(curl, CURLOPT_URL, dstFilePath);

	// Pass in the open FILE pointer to the file to write to HCP.
	srcFile = fopen(srcFilePath, "rb");
	if(!srcFile) {
		fprintf(stderr, "Unable to open file %s\n", srcFilePath);
		retval = EXIT_FAILURE;
		goto cleanup;
	}
	curl_easy_setopt(curl, CURLOPT_READDATA, srcFile);

	// Use this HCP Authorization header to perform authentication.
	httpHeaderList = curl_slist_append(httpHeaderList, authHeader);
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, httpHeaderList);

	/***
	 *** Execute the HTTP PUT request.
	 ***/

	CURLcode curlRetVal = curl_easy_perform(curl);
	if(curlRetVal != CURLE_OK) {
		fprintf(stderr, "Error writing %s to HCP!\nReturn Code: %d\n", srcFilePath, curlRetVal);
		retval = EXIT_FAILURE;
		goto cleanup;
	}

	/***
	 *** Process the HTTP Response.
	 ***/

	long responseCode = 0;

	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);

	// Any code not in the 200 range signals a failure.
	if (2 != responseCode / 100) {
		fprintf(stderr, "ERROR: Unexpected response code from server (%d). Operation failed.\n", (int)responseCode);
		retval = EXIT_FAILURE;
	}

  cleanup:

    /* Close the source File */
    if (NULL != srcFile) {
	  fclose(srcFile);
    }

    /*
     *  Even though the curl handle has a pointer to it, we still own this list.
     */
    if (httpHeaderList) {
    	curl_slist_free_all(httpHeaderList);

    	// Clear out the HTTP Header pointer we setup just in case this curl connection
    	// is used for another follow up operation.
    	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
    }

	return retval;
}

/**
 * This routine performs and HTTP GET of an object.
 *
 */
int do_read(CURL *curl, const char *srcFilePath, const char *dstFilePath, const char *authHeader) {

	int retval = EXIT_SUCCESS;
	FILE *dstFile = NULL;
	struct curl_slist *httpHeaderList=NULL;

	/***
	 *** Setup the CURL object for performing the GET request.
	 ***/

	// Reset curl to clear out prior usage.
	curl_easy_reset(curl);

	// Bypass any SSL Key checks because we are not using site specific keys.
    //  Not needed if only doing HTTP
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);

	// Provide the response header call back function.  This is just for
	//  debugging/development purposes. It will cause the HTTP headers for
	//  the response to be output to stdout.
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &headerProcessor);

	// Indicate this CURL operation is an HTTP GET.
	curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);

	// Put decompressed content into this file. The libcurl implementation
	//   will handle all file I/O using stock stdio operations.
	dstFile = fopen(dstFilePath, "wb");
	if(!dstFile) {
		fprintf(stderr, "Unable to open file %s\n", dstFilePath);
		retval = EXIT_FAILURE;
		goto cleanup;
	}

	curl_easy_setopt(curl, CURLOPT_WRITEDATA, dstFile);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); // Use the default function

	// Set the URL to the source object on HCP
	curl_easy_setopt(curl, CURLOPT_URL, srcFilePath);

	// Use this HCP Authorization header to perform authentication.
	httpHeaderList = curl_slist_append(httpHeaderList, authHeader);
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, httpHeaderList);

	/***
	 *** Execute the HTTP GET request.
	 ***/

	CURLcode curlRetVal = curl_easy_perform(curl);
	if(curlRetVal != CURLE_OK) {
		fprintf(stderr, "Error reading %s from HCP!\nReturn Code: %d\n", srcFilePath, curlRetVal);
		retval = EXIT_FAILURE;
		goto cleanup;
	}

	/***
	 *** Process the HTTP Response.
	 ***/

	long responseCode = 0;

	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);

	// Any code not in the 200 range signals a failure.
	if (2 != responseCode / 100) {
		fprintf(stderr, "ERROR: Unexpected response code from server (%d). Operation failed.\n", (int)responseCode);
		retval = EXIT_FAILURE;
	}

  cleanup:

    /* Close the destination File */
    if (NULL != dstFile) {
  	  fclose(dstFile);

      if (EXIT_SUCCESS != retval)
        unlink(dstFilePath);  // Remove any residual file.
    }

    /*
     *  Even though the curl handle has a pointer to it, we still own this list.
     */
    if (httpHeaderList) {
    	curl_slist_free_all(httpHeaderList);

    	// Clear out the HTTP Header pointer we setup just in case this curl connection
    	// is used for another follow up operation.
    	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
    }

	return retval;
}

/**
 * This program is the main line for an example on writing (PUT) and reading (GET)
 * content with HCP.
 */
int main(int argc, char **argv) {
	int retval = EXIT_SUCCESS;

	/****
	 **** Make sure we have the required parameters.
	 ****/
	if (argc != 4) {
		char *basename = strrchr(argv[0], '/');
		if (NULL == basename)
			basename = argv[0];
		else
			basename++;

		fprintf(stderr, "\nUsage: %s <DNS-Namespace> <UserName> <Password>\n", basename);
		fprintf(stderr, "  where <DNS-Namespace> is the fully qualified DNS name of the HCP Namespace.\n");
		fprintf(stderr, "              For example: \"ns1.ten1.myhcp.example.com\"\n");
		fprintf(stderr, "        <Username> and <Password> are the credentials of the HCP user with data\n");
		fprintf(stderr, "              access permissions for the namespace.\n\n");

		exit(EXIT_FAILURE);
	}

	/****
	 **** Initialize the execution environment for HTTP over libcurl
	 ****/

	/* platform-specific setup */
	curl_global_init(CURL_GLOBAL_ALL);

	/* Build the HCP file path. */
	char *HCPFilePath = malloc( strlen(HCP_PROTOCOL) + 3
			+ strlen(argv[1]) + 1 + strlen(HCP_FILE_PATH) + 1 );
	sprintf(HCPFilePath, "%s://%s/%s", HCP_PROTOCOL, argv[1], HCP_FILE_PATH);

	CURL *curl = NULL; /* curl session handle */

	// Use this HCP security cookie to perform authentication.
	char *authHeader = createAuthHeader(argv[2], argv[3]);

	/*
	 * Create a curl session to use for the operations.
	 */
	curl = curl_easy_init();
	if(!curl) {
		fprintf(stderr, "Unable to initialize cURL HTTP library\n");

		retval = EXIT_FAILURE;
		goto cleanup;
	}

	/****
	 **** Perform a write (PUT) of the content on HCP.
	 ****/
	retval = do_write(curl, INPUT_FILE_NAME, HCPFilePath, authHeader);
	if (EXIT_SUCCESS != retval) {
		fprintf(stderr, "FAILURE: do_write() failed!!\n");

		retval = EXIT_FAILURE;
		goto cleanup;
	}

	/****
	 **** Perform a read (GET) of the content from HCP.
	 ****/
	retval = do_read(curl, HCPFilePath, OUTPUT_FILE_NAME, authHeader);
	if (EXIT_SUCCESS != retval) {
		fprintf(stderr, "FAILURE: do_read() failed!!\n");

		retval = EXIT_FAILURE;
		goto cleanup;
	}

	/****
	 **** SUCCESS!!  Just clean-up and get outta here.
	 ****/
cleanup:
	if (HCPFilePath) free(HCPFilePath);

	if (authHeader) free(authHeader);

    /* free curl session */
	if(curl) {
		curl_easy_cleanup(curl);
		curl = NULL;
	}

	/* free platform bindings */
	curl_global_cleanup();

	return EXIT_SUCCESS;
}
